package com.hockeo.server.servlet.convert;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import com.hockeo.client.model.JSData;
import com.hockeo.server.data.DAO;
import com.hockeo.server.utils.VHRuntimeException;

/**
 * Converts GWT client side entities into Java server entities.
 *
 * @version $Id$
 * @author jjanke
 */
public class Gwt2JConverter
{
  private final DAO d_dao;

  /**
   * Creates a new Gwt2JConverter.
   *
   * @param dao the DAO object to be used for communicating with the datastore
   */
  Gwt2JConverter( DAO dao )
  {
    d_dao = dao;
  }

  public GwtEnabled convert( JSData src )
  {
    final String strClassName = src.getServerClass();

    if ( strClassName == null )
      throw new IllegalArgumentException( "The given JSData instance '" + src.toString() + "' does not define a server class." );

    GwtEnabled dest = (GwtEnabled) d_dao.getTGDSFactory().createNewUninitialisedInstance( strClassName );

    dest.fromGwt( src );

    return dest;
  }

  public List<GwtEnabled> convert( List<JSData> listSrc )
  {
    List<GwtEnabled> listDest = new ArrayList<GwtEnabled>();

    for ( JSData src : listSrc )
      listDest.add( convert( src ) );

    return listDest;
  }

  /**
   * <p>
   * Tries to retrieve the argument-less constructor of the class. First it tries to get
   * hold of any declared constructor directly declared in the given class. If the
   * constructor is non-public, {@link Constructor#setAccessible(boolean)} is called to
   * make it accessible. If there is no declared constructor, we check the whole class
   * hierarchy for a reachable argument-less constructor.
   * </p>
   *
   * <p>
   * Using an accessible private constructor is much more performant than a public
   * constructor. Therefore this method always checks first if such a constructor is
   * available.
   * </p>
   *
   * @param cls the class for which to retrieve a constructor
   * @return a matching constructor object or <code>null</code> if there is none available
   */
  private Constructor<? extends GwtEnabled> getConstructor( Class<? extends GwtEnabled> cls )
  {
    Constructor<? extends GwtEnabled> c;

    try
    {
      c = cls.getDeclaredConstructor();

      if ( ( c.getModifiers() & Modifier.PUBLIC ) == 0 )
        c.setAccessible( true );
    }
    catch ( SecurityException ex )
    {
      throw new VHRuntimeException( ex );
    }
    catch ( NoSuchMethodException ex )
    {
      // we will try finding a constructor that is possibly defined in a super class
      //
      try
      {
        c = cls.getConstructor();
      }
      catch ( SecurityException ex1 )
      {
        throw new VHRuntimeException( ex );
      }
      catch ( NoSuchMethodException ex1 )
      {
        // no matching constructor found
        //
        return null;
      }
    }

    return c;
  }
}
